//
//  ConfigScriptViewController.swift
//  Hermes
//
//  Created by FH on 2021/1/19.
//  Copyright © 2021 Agora. All rights reserved.
//

import Cocoa

enum APIsCategory : String {
    case ObjcAPIs = "ObjcAPIs"
    case AppContext = "AppContext"
    case CppAPIs = "CppAPIs"
    
    init(caseIndex: Int) {
        if caseIndex == 2 {
            self = .CppAPIs
        } else if caseIndex == 1 {
            self = .AppContext
        } else {
            self = .ObjcAPIs
        }
    }
    
    static func toList() -> [String] {
        return [APIsCategory.ObjcAPIs.rawValue, APIsCategory.AppContext.rawValue, APIsCategory.CppAPIs.rawValue]
    }
}

// TODO: move to APIsTagsHandler
enum ObjcAPIsEntryType : String {
    case All = "All"
    case AgoraRtcEngineKit = "AgoraRtcEngineKit"
    case AgoraRtcEngineDelegate = "AgoraRtcEngineDelegate"
    case AgoraRtcChannel = "AgoraRtcChannel"
    case AgoraRtcChannelDelegate = "AgoraRtcChannelDelegate"
    
    init(caseIndex: Int) {
        if caseIndex == 4 {
            self = .AgoraRtcChannelDelegate
        } else if caseIndex == 3 {
            self = .AgoraRtcChannel
        } else if caseIndex == 2 {
            self = .AgoraRtcEngineDelegate
        } else if caseIndex == 1 {
            self = .AgoraRtcEngineKit
        } else {
            self = .All
        }
    }
    
    static func toList() -> [String] {
        return [ObjcAPIsEntryType.All.rawValue, ObjcAPIsEntryType.AgoraRtcEngineKit.rawValue, ObjcAPIsEntryType.AgoraRtcEngineDelegate.rawValue,
                ObjcAPIsEntryType.AgoraRtcChannel.rawValue, ObjcAPIsEntryType.AgoraRtcChannelDelegate.rawValue]
    }
}

//////////////////////////////// TODO: remove

class ConfigScriptContext: PropsStatesContext {
    private let localStates = StatesStore()
    // TODO: remove
    fileprivate var isInited = false
    fileprivate var cachedSelectedIndex = [String : Int]()
    fileprivate var apisCategory: APIsCategory = .ObjcAPIs
    // for search objc APIs
    fileprivate var objcAPIsEntryType: ObjcAPIsEntryType? = nil
    fileprivate var selectObjcTagTypes: NSPopUpButton? = nil
    // for Props-States
    @PropState
    var searchDataSource: [AstNode]! = nil
    @PropState
    var searchKey: String? = nil
    
    init() {
        self.bindStates()
    }
    
    fileprivate func reset() {
        self.$searchDataSource.update(value: nil, localStates)
        self.$searchKey.update(value: nil, localStates)
    }
    
    fileprivate func setSerchDataSource(_ dataSource: [AstNode]) {
        self.$searchDataSource.update(value: dataSource, self.localStates)
    }
    
    func setSearchKey(_ key: String) {
        self.$searchKey.update(value: key, self.localStates)
    }
}

////////////////////////////////
// TODO: modify
let constAPIsSearchViewHeight: Float = 40

class ConfigScriptViewController : NSViewController {
    let appContext = HermesContext()
    let states = ConfigScriptContext()
    let yoga = FLContext()
    var properties: MainContext! = nil
    var bodyView = NSView()
    var tblScript: FLTableView<String, VoidRowCell>! = nil
    var tblAPIs: FLTableView<AstNode, APIsMethodRowCellView>! = nil
    var scrollEditParams: NSScrollView = NSScrollView()
    // for buttons
    let btnCancel = NSButton()
    let btnSave = NSButton()
    let btnAttach = NSButton()
    // for search APIs
    let selectAPIsType = NSPopUpButton()
    let selectCategory = NSPopUpButton()
    let inputSearch = FLInputField()
    var popRecommendResult: NSPopover? = nil
    let btnSearch = NSButton()
    /*
     ObjcAPIs:   [0]apisEntryType、[1]tagsType、[2]tagGroups、[3]tagOrder、[4]tagScene
     AppContext:
     */
    let associatedSelects = [NSPopUpButton(), NSPopUpButton(), NSPopUpButton(), NSPopUpButton(), NSPopUpButton()]
    
    convenience init(_ properties: MainContext) {
        self.init()
        self.properties = properties
    }
    
    override func loadView() {
        self.view = NSView(frame: CGRect(x: 0, y: 0, width: 1440, height: 808))
    }
    
    override func viewDidLayout() {
        if !states.isInited, let window = NSApplication.shared.mainWindow {
            window.title = "Config Script"
            states.isInited = true
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.dataBinding()
        self.layoutViews()

        // associated selects databind and select default index
        for index in 0..<associatedSelects.count {
            let select = associatedSelects[index]

            var signal = select.rac_eventTouch
            // default select apisEntryType == AgoraRtcEngineKit
            if index == 0 {
                select.selectItem(at: 1)
                signal = signal.start(with: select)
            }
            signal.subscribeNext {[weak self] select in
                if let self = self {
                    self.responseAssociatedSelected(nil,
                                                    associatedSelectedIndex: select.indexOfSelectedItem,
                                                    associatedSelectViewPos: index)
                }
            }
        }
    }
    
    func dataBinding() {
        // search recommend result
        states.$searchKey.signal().ignoreNilValue().subscribeNext {[weak self] val in
            if let self = self {
                self.popRecommendResult!.close()
                self.popRecommendResult = nil
                self.inputSearch.flText = val!
                self.inputSearch.focus()
            }
        }
        
        self.inputSearch.rac_valueDidChanged.filter { val in
            if val.count > 2 {
                return true
            } else if let popover = self.popRecommendResult {
                popover.close()
                self.popRecommendResult = nil
            }
            return false
        }.subscribeNext {[weak self] str in
            if let self = self {
                let nodes = self.searchObjCAPIs(withSearchKey: true)
                self.states.setSerchDataSource(nodes)
                if self.popRecommendResult == nil {
                    if nodes.count > 0 {
                        let contentVC = SearchResultViewControllerTODOREMOVE(self.states)
                        self.states.reset()
                        self.popRecommendResult = FLPopover(contentVC)
                        self.popRecommendResult!.show(self.inputSearch, .maxY)
                        self.inputSearch.focus()
                    } else {
                        self.popRecommendResult!.close()
                        self.popRecommendResult = nil
                    }
                }
            }
        }
        // select category
        self.selectCategory.addItems(withTitles: APIsCategory.toList())
        self.selectCategory.rac_eventTouch.start(with: self.selectCategory).subscribeNext {[weak self] select in
            self?.responseAssociatedSelected(select.indexOfSelectedItem,
                                             associatedSelectedIndex: nil,
                                             associatedSelectViewPos: nil)
        }
        // select AstNodeTypes
        self.selectAPIsType.rac_eventTouch.subscribeNext {[weak self] select in
            self?.searchObjCAPIs(withSearchKey: true)
            // TODO: 左右两侧条件必须有一个为.all
        }
        // tables
        self.tblAPIs = buildRowCellTableView(yoga, APIsMethodRowCellView.self)
        // buttons
        self.btnSearch.rac_eventTouch.subscribeNext {[weak self] _ in
            self?.searchObjCAPIs(withSearchKey: true)
        }
        
        self.btnCancel.rac_eventTouch.subscribeNext {[weak self] _ in
            if let self = self {
                self.properties.exitScriptSetting(hasSave: false)
            }
        }

        self.btnSave.rac_eventTouch.subscribeNext {[weak self] _ in
            if let self = self {
                self.properties.exitScriptSetting(hasSave: true)
            }
        }
        
        self.btnAttach.rac_eventTouch.subscribeNext {[weak self] _ in
            if let self = self {
                
            }
        }
    }
    
    func layoutViews() {
        self.view.bindAsRoot(to: yoga)
        
        self.buildAPIsSearchView()
        self.buildTableScriptView()
        self.buildTableAPIsView()
        self.buildEditParamsView()
        
        yoga.apply()
    }
    
    // search apis
    func searchObjCAPIs(withSearchKey: Bool, withAssociatedTagCaseIndex: Int?=nil) -> [AstNode] {
        if withSearchKey {
            let searchKey = self.inputSearch.flText.count == 0 ? nil : self.inputSearch.flText
            let apiTypeCaseIndex = self.states.apisCategory != .AppContext ? self.selectAPIsType.indexOfSelectedItem : nil
            return self.appContext.apisTags.fuzzySearchAPIsName(searchKey, apiTypeCaseIndex)
        } else {
            if let apisEntryType = self.states.objcAPIsEntryType {
                if let tagType = AstNodeTagType(caseIndex: self.states.selectObjcTagTypes?.indexOfSelectedItem) {

                }
            }
            return []
        }
    }
    
    func setRecommendObjCAPI() {
        
    }
    
    // REMARK: response associated selected
    
    func responseAssociatedSelected(_ categoryItemIndex: Int?,
                                    associatedSelectedIndex: Int?, associatedSelectViewPos: Int?) {
        if let categoryItemIndex = categoryItemIndex {
            if let oldIndex = states.cachedSelectedIndex["apisCategory"], oldIndex == categoryItemIndex {
                return
            } else {
                states.cachedSelectedIndex.removeAll()
                states.cachedSelectedIndex["apisCategory"] = categoryItemIndex
            }
            // TODO: display .none
            self.selectAPIsType.removeAllItems()
            for select in self.associatedSelects {
                select.removeAllItems()
            }
            states.apisCategory = APIsCategory(caseIndex: categoryItemIndex)
            switch states.apisCategory {
            case .ObjcAPIs:
                self.selectAPIsType.addItems(withTitles: AstNodeType.toList())
                
                let selectAPIs = self.associatedSelects[0]
                selectAPIs.addItems(withTitles: ObjcAPIsEntryType.toList())
            case .AppContext:
                let selectAPIs = self.associatedSelects[0]
                selectAPIs.removeAllItems()
                print("TODO:")
            case .CppAPIs:
                print("TODO:")
            }
        } else if let selectedIndex = associatedSelectedIndex, let selectViewPos = associatedSelectViewPos {
            switch states.apisCategory {
            case .ObjcAPIs:
                // select apisEntryType
                if selectViewPos == 0 {
                    let key = self.getAssociatedSelectCachedKey(selectViewPos)
                    if let oldIndex = states.cachedSelectedIndex[key], oldIndex == selectedIndex {
                        return
                    } else {
                        states.cachedSelectedIndex[key] = selectedIndex
                        // select `all` item
                        if selectedIndex == 0 {
                            self.cleanObjcAPIsEntryAssociatedCachedKey()
                        }
                    }
                    states.objcAPIsEntryType = ObjcAPIsEntryType(caseIndex: selectedIndex)
                    self.cleanAssociatedSelects(startIndex: 1)
                    states.selectObjcTagTypes = nil
                    if states.objcAPIsEntryType! == .AgoraRtcEngineKit
                        || states.objcAPIsEntryType! == .AgoraRtcEngineDelegate {
                        self.responseTagTypeChange()
                    } else {
                        self.searchObjCAPIs(withSearchKey: false)
                    }
                } else if selectViewPos == 1 {
                    self.responseTagTypeChange()
                    self.searchObjCAPIs(withSearchKey: false)
                } else {
                    let key = self.getAssociatedSelectCachedKey(selectViewPos)
                    if let oldIndex = states.cachedSelectedIndex[key], oldIndex == selectedIndex {
                        return
                    } else {
                        states.cachedSelectedIndex[key] = selectedIndex
                    }
                    self.searchObjCAPIs(withSearchKey: false, withAssociatedTagCaseIndex: selectedIndex)
                }
            case .AppContext:
                print("TODO:")
            case .CppAPIs:
                print("TODO:")
            }
        }
    }
    
    func cleanAssociatedSelects(startIndex: Int) {
        for index in startIndex..<self.associatedSelects.count {
            let select = self.associatedSelects[index]
            select.removeAllItems()
            self.setAssociatedSelectDisplay(select, hidden: true)
        }
    }
    
    func setAssociatedSelectDisplay(_ select: NSPopUpButton, hidden: Bool) {
        if let layoutNode = self.yoga.getNode(by: select) {
            layoutNode.display = hidden ? .none : .flex
        }
        select.isHidden = hidden
    }
    
    func getAssociatedSelectCachedKey(_ selectViewPos: Int) -> String {
        if selectViewPos == 0 {
            return "\(states.apisCategory.rawValue)_APIsEntryType"
        } else if selectViewPos == 1 {
            return "\(states.apisCategory.rawValue)_\(states.objcAPIsEntryType!.rawValue)_TagTypes"
        } else {
            return "\(states.apisCategory.rawValue)_\(states.objcAPIsEntryType!.rawValue)_\(states.selectObjcTagTypes!.indexOfSelectedItem)_\(selectViewPos)"
        }
    }
    
    func cleanObjcAPIsEntryAssociatedCachedKey() {
        let agoraInterfaceKey = "\(states.apisCategory.rawValue)_\(ObjcAPIsEntryType.AgoraRtcEngineKit)_"
        let agoraDelegateKey = "\(states.apisCategory.rawValue)_\(ObjcAPIsEntryType.AgoraRtcEngineDelegate)_"
        var indexes = [Dictionary<String, Int>.Index]()
        for key in self.states.cachedSelectedIndex.keys {
            if key.hasPrefix(agoraInterfaceKey) || key.hasPrefix(agoraDelegateKey) {
                indexes.append(self.states.cachedSelectedIndex.index(forKey: key)!)
            }
        }
        for index in indexes {
            self.states.cachedSelectedIndex.remove(at: index)
        }
    }
    
    func responseTagTypeChange() {
        let selectTagTypes = self.associatedSelects[1]
        let selectedIndex = selectTagTypes.indexOfSelectedItem == -1 ? 0 : selectTagTypes.indexOfSelectedItem
        let key = self.getAssociatedSelectCachedKey(1)
        if let oldIndex = states.cachedSelectedIndex[key], oldIndex == selectedIndex {
            return
        } else {
            states.cachedSelectedIndex[key] = selectedIndex
        }
        self.cleanAssociatedSelects(startIndex: 2)
        
        if states.selectObjcTagTypes == nil {
            // detect can load tagTypes, load groups logic == load nodeTagTypes logic
            let tagTypes = appContext.apisTags.getNodeTagTypes(apisType: states.objcAPIsEntryType!.rawValue)
            if tagTypes.count > 0 {
                selectTagTypes.addItems(withTitles: appContext.apisTags.getNodeTagTypes(apisType: states.objcAPIsEntryType!.rawValue))
                self.setAssociatedSelectDisplay(selectTagTypes, hidden: false)
            }
            states.selectObjcTagTypes = selectTagTypes
        }
        // load tag groups
        if selectTagTypes.indexOfSelectedItem == 0,
            let groups = appContext.apisTags.getGroupTags(apisEntryType: states.objcAPIsEntryType!.rawValue) {
            let key = self.getAssociatedSelectCachedKey(2)
            if let oldIndex = states.cachedSelectedIndex[key], oldIndex == 0 {
                return
            } else {
                states.cachedSelectedIndex[key] = 0
            }
            let selectGroups = self.associatedSelects[2]
            selectGroups.addItems(withTitles: groups)
            self.setAssociatedSelectDisplay(selectGroups, hidden: false)
        }
        // load tag scene and order
        if states.objcAPIsEntryType == .AgoraRtcEngineKit {
            if selectTagTypes.indexOfSelectedItem == 1 {
                let key = self.getAssociatedSelectCachedKey(3)
                if let oldIndex = states.cachedSelectedIndex[key], oldIndex == 0 {
                    return
                } else {
                    states.cachedSelectedIndex[key] = 0
                }
                let selectOrder = self.associatedSelects[3]
                selectOrder.addItems(withTitles: AstNodeTagOrder.toList())
                self.setAssociatedSelectDisplay(selectOrder, hidden: false)
            } else if selectTagTypes.indexOfSelectedItem == 2 {
                let key = self.getAssociatedSelectCachedKey(4)
                if let oldIndex = states.cachedSelectedIndex[key], oldIndex == 0 {
                    return
                } else {
                    states.cachedSelectedIndex[key] = 0
                }
                let selectScene = self.associatedSelects[4]
                selectScene.addItems(withTitles: AstNodeTagScene.toList())
                self.setAssociatedSelectDisplay(selectScene, hidden: false)
            }
        }
        // detect had layout
        if let _ = self.yoga.getNode(by: self.view) {
            self.yoga.apply()
        }
    }
}
